//
// Created by yinlijun on 17-10-1.
//

#include <string.h>
#include <unistd.h>
#include <endian.h>
#include <sys/socket.h>
#include "write_data_record.h"
#include "app_sys_log.h"

struct WriteDataRecord {
    int fd;
    void *data;
    size_t data_size;
    size_t written_size;
    int header_send_size;
};

WriteDataRecord* write_data_record_create(int fd, void* data, size_t data_size) {
    WriteDataRecord* thiz = (WriteDataRecord *)malloc(sizeof(WriteDataRecord));
    thiz->data = (void*)malloc(data_size);
    thiz->data_size = data_size;
    thiz->fd = fd;
    thiz->written_size = 0;
    thiz->header_send_size = 0;
    memcpy(thiz->data, data, data_size);

    return thiz;
}

static int write_data_record_write_header(WriteDataRecord* thiz) {
    uint32_t header = htonl(thiz->data_size);
    int count = send(thiz->fd, (void *)&header + thiz->header_send_size, 4, MSG_NOSIGNAL | MSG_CONFIRM);

    if(count <= 0) {
        return count;
    }

    thiz->header_send_size += count;

    return count;
}


static int write_data_record_header_send_done(WriteDataRecord *thiz) {
    return thiz->header_send_size >= 4;
}

int write_data_record_finished(WriteDataRecord* thiz) {
    return write_data_record_header_send_done(thiz) && thiz->written_size >= thiz->data_size;
}

int write_data_record_write(WriteDataRecord* thiz) {
    if(!write_data_record_header_send_done(thiz)) {
        int ret = write_data_record_write_header(thiz);
        if(!write_data_record_header_send_done(thiz)) {
            return ret;
        }
    }

    void *data = thiz->data;
    size_t data_size = thiz->data_size;
    size_t written_size = thiz->written_size;
    int write_count = 0;
    if(data_size > written_size) {
        write_count = send(thiz->fd, data + written_size, data_size - written_size, MSG_NOSIGNAL);
        //app_sys_debug("%s %p write %d bytes", __func__, thiz, write_count);
    }
    if(write_count > 0) {
        thiz->written_size += write_count;
    }
    return write_count;
}

void* write_data_record_get_data(WriteDataRecord* thiz) {
    return thiz->data;
}

size_t write_data_record_get_size(WriteDataRecord* thiz) {
    return thiz->data_size;
}

size_t write_data_record_get_written(WriteDataRecord* thiz) {
    return thiz->written_size;
}

void write_data_record_destroy(WriteDataRecord* thiz) {
    if(thiz != NULL) {
        if (thiz->data != NULL) {
            free(thiz->data);
        }
        thiz->data = NULL;
        free(thiz);
    }
    return;
}
